package com.jdd.modules.sdkidentifyresultpushlog.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jdd.common.api.vo.Result;
import com.jdd.common.constant.JddConstant;
import com.jdd.common.exception.JddException;
import com.jdd.common.util.ChargeFeeUtils;
import com.jdd.common.util.DateUtils;
import com.jdd.common.util.ResultCode;
import com.jdd.common.util.SnowFlakeUtil;
import com.jdd.modules.configcartype.entity.ConfigCarType;
import com.jdd.modules.configcartype.mapper.ConfigCarTypeMapper;
import com.jdd.modules.order.entity.ParkOrderInfo;
import com.jdd.modules.order.mapper.ParkOrderInfoMapper;
import com.jdd.modules.parking.entity.*;
import com.jdd.modules.parking.mapper.ParkCarEnterLogMapper;
import com.jdd.modules.parking.mapper.ParkCarOutLogMapper;
import com.jdd.modules.parking.mapper.ParkInfoMapper;
import com.jdd.modules.parking.service.IConfigParkInfoService;
import com.jdd.modules.parking.service.IParkArmInfoService;
import com.jdd.modules.parking.service.IParkWorkStationService;
import com.jdd.modules.sdkidentifyresultpushlog.entity.ParkOrderInfoVO;
import com.jdd.modules.sdkidentifyresultpushlog.mapper.SdkIdentifyResultPushLogMapper;
import com.jdd.modules.sdkidentifyresultpushlog.service.ChargeFeeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 计费服务接口实现
 *
 * @author liuyaowen
 * @date 2021-07-14
 */
@Service
@Slf4j
public class ChargeFeeServiceImpl implements ChargeFeeService {
    @Resource
    private ParkCarEnterLogMapper parkCarEnterLogMapper;
    @Resource
    private IParkArmInfoService parkArmInfoService;
    @Resource
    private IParkWorkStationService parkWorkStationService;
    @Resource
    private ParkInfoMapper parkInfoMapper;
    @Resource
    private IConfigParkInfoService configParkInfoService;
    @Resource
    private ParkOrderInfoMapper parkOrderInfoMapper;
    @Resource
    private ConfigCarTypeMapper configCarTypeMapper;
    @Resource
    private ParkCarOutLogMapper parkCarOutLogMapper;
    @Resource
    private ChargeFeeUtils chargeFeeUtils;
    @Resource
    private SdkIdentifyResultPushLogMapper sdkIdentifyResultPushLogMapper;

    /**
     * 该方法是有牌车出口码计费入口,在该方法中将在对入场纪录做处理之后开始计费
     */
    @Override
    public Result<?> outParkChargeFee(String parkCode, String plateNo, String serialNo, String imgPath) {
        log.info("计费服务类,出场码计费,传入参数:车场编号:{},车牌号:{},设备序列号:{}", parkCode, plateNo, serialNo);

        try {
            ParkArmInfo parkArmInfo = parkArmInfoService.findParkArmInfoBySerialNo(serialNo);
            if (parkArmInfo == null) {
                throw new JddException(ResultCode.PARAMETER_ERROR, "车道信息不存在");
            }
            Date outTime = new Date();
            String outTimeStr = DateUtil.format(outTime, "yyyy-MM-dd HH:mm:ss");
            List<ParkCarEnterLog> carAllEnterLogs = new ArrayList<>();
            try {
                carAllEnterLogs = this.getParkCarEnterLog(plateNo, null, parkCode);
            } catch (JddException e) {
                if (e.getCode() == ResultCode.PARAMETER_ERROR) {
                    log.info("车辆无入场记录");
                    ConfigParkInfo configParkInfo = configParkInfoService.findConfigParkInfoByParkCode();
                    if (null != configParkInfo) {
                        // AdmissionRecordsCar 无入场记录是否开闸  1:不开闸 2:免费放行 3:自动补录n小时入场记录
                        if (JddConstant.ConfigParkInfo.ADMISSION_RECORDS_CAR_TWO.equals(configParkInfo.getAdmissionRecordsCar())) {
                            ParkCarOutLog carOutLog = new ParkCarOutLog();
                            carOutLog.setId(SnowFlakeUtil.getId());
                            carOutLog.setSerialNo(serialNo);
                            carOutLog.setParkCode(parkCode);
                            carOutLog.setImgPath(imgPath);
                            carOutLog.setParkName(parkArmInfo.getParkName());
                            carOutLog.setPlateNo(plateNo);
                            carOutLog.setArmName(parkArmInfo.getArmName());
                            carOutLog.setCreateTime(new Date());
                            carOutLog.setCarOutTime(outTimeStr);
                            carOutLog.setCarType("1");
                            carOutLog.setRemark("未查找到入场记录免费放行!");
                            //加上工作站名和岗亭人账户
                            String workUser = parkWorkStationService.getStationOperationNameByParkArm(parkArmInfo);
                            if(ObjectUtil.isNotEmpty(workUser)){
                                carOutLog.setWorkName(parkArmInfo.getWorkStationName());
                                carOutLog.setSysUserName(workUser);
                            }
                            parkCarOutLogMapper.insert(carOutLog);
                            return Result.ok().setCode(JddConstant.AdmissionRecordsCarType.FREE_RELEASE_CODE);
                        }
                        if (JddConstant.ConfigParkInfo.ADMISSION_RECORDS_CAR_ONE.equals(configParkInfo.getAdmissionRecordsCar())) {
                            return Result.ok().setCode(JddConstant.AdmissionRecordsCarType.NO_OPEN_CODE);
                        }
                    }
                }
            }
            // 得到车辆的第一次入场纪录
            ParkCarEnterLog firstEnterLog = carAllEnterLogs.get(0);
            // 使用车辆出场识别信息制作一个入场假纪录辅助计费
            ParkCarEnterLog lastEnterLog = new ParkCarEnterLog();
            lastEnterLog.setParkCode(parkCode);
            lastEnterLog.setCarEnterTime(outTimeStr);
            carAllEnterLogs.add(lastEnterLog);
            // 根据第一次入场纪录,得到车辆类型
            String carTypeId = getCarTypeId(parkCode, firstEnterLog.getCarTypeNo());
            ParkOrderInfoVO parkOrderInfoVO = this.chargeFee(carAllEnterLogs, parkArmInfo, carTypeId, outTime);
            //入场抓拍图片地址
            parkOrderInfoVO.setImgPath(firstEnterLog.getImgPath());
            //停车场入口名称
            parkOrderInfoVO.setEnterArmName(firstEnterLog.getArmName());
            //入场云图片地址
            parkOrderInfoVO.setOssImgPath(firstEnterLog.getOssImgPath());

            parkOrderInfoVO.setOutImgPath(imgPath);
            log.info("计费服务类,出场码计费结果：{}", parkOrderInfoVO);
            return Result.ok(parkOrderInfoVO);
        } catch (Exception e) {
            log.error("计费服务类,异常信息", e);
            throw new JddException(ResultCode.INTERNAL_SERVER_ERROR, "计费服务类,出场码计费系统错误," + e.getMessage());
        }
    }

    @Override
    public Result<?> chargeFeeAgain(String serialNo,String plateNo,String carTypeId)
    {
        log.info("计费服务类,指定车辆类型重新计费,传入参数:车道设备序列号:{},车牌号:{},车辆类型id:{}", serialNo, plateNo,carTypeId);
        try {
            ParkArmInfo parkArmInfo = parkArmInfoService.findParkArmInfoBySerialNo(serialNo);
            String parkCode = parkArmInfo.getParkCode();
            Date outTime = new Date();
            String outTimeStr = DateUtil.format(outTime, "yyyy-MM-dd HH:mm:ss");
            List<ParkCarEnterLog> carAllEnterLogs = new ArrayList<>();
            try {
                carAllEnterLogs = this.getParkCarEnterLog(plateNo, null, parkCode);
            } catch (JddException e) {
                if (e.getCode() == ResultCode.PARAMETER_ERROR) {
                    log.error("车辆无入场记录");
                    return Result.error("车辆无入场记录");
                }else {
                    log.error("算费服务异常："+e.getMessage());
                    return Result.error(e.getMessage());
                }
            }
            // 使用车辆出场识别信息制作一个入场假纪录辅助计费
            ParkCarEnterLog lastEnterLog = new ParkCarEnterLog();
            lastEnterLog.setParkCode(parkCode);
            lastEnterLog.setCarEnterTime(outTimeStr);
            carAllEnterLogs.add(lastEnterLog);
            ParkOrderInfoVO parkOrderInfoVO = this.chargeFee(carAllEnterLogs,parkArmInfo,carTypeId,outTime);
            //返回订单信息，因为需要传到云端
            log.info("计费服务类,出场码计费结果：{}", parkOrderInfoVO);
            return Result.ok("计费成功").setResult(parkOrderInfoVO);
        } catch (Exception e) {
            log.error("计费服务类,异常信息", e);
            throw new JddException(ResultCode.INTERNAL_SERVER_ERROR, "计费服务类,出场码计费系统错误," + e.getMessage());
        }
    }


    private ParkOrderInfoVO chargeFee(List<ParkCarEnterLog> carAllEnterLogs,ParkArmInfo parkArmInfo,String carTypeId,Date outTime){
        long unPayFee = 0;
        ParkCarEnterLog firstEnterLog = carAllEnterLogs.get(0);
        // 遍历入场纪录,对车辆在每个车场内的停车时间进行算费。
        long chargeFee = chargeFeeUtils.chargeTotalFee(carAllEnterLogs, carTypeId);
        // 根据最近的入场记录id查询对应的支付订单
        Map map = getOrderInfo(firstEnterLog.getId());
        // 得到车辆的已支付金额
        int paidFee = (int) map.get("paidFee");
        List<ParkOrderInfo> orderInfos = (List<ParkOrderInfo>) map.get("orderInfoList");
        // 如果存在车辆的已支付订单
        if (CollectionUtils.isNotEmpty(orderInfos)) {
            // 得到最近支付的一条订单信息
            ParkOrderInfo order = orderInfos.get(0);
            // 得到支付时间
            Date payTime = order.getCreateTime();
            long time = DateUtil.between(payTime, outTime, DateUnit.MINUTE);
            ParkInfo parkInfo = parkInfoMapper.getFreeMinutes();
            int freeMinutes = parkInfo.getFreeMinutes();
            if (time > freeMinutes) {
                unPayFee = chargeFee - paidFee;
            }
        } else {
            unPayFee = chargeFee;
        }
        unPayFee = unPayFee<0?0:unPayFee;
        try {
            Date carEnterTime = DateUtils.stringToDate(firstEnterLog.getCarEnterTime(), "yyyy-MM-dd HH:mm:ss");
            ParkOrderInfoVO parkOrderInfoVO = new ParkOrderInfoVO();
            Date endDate = new Date();
            parkOrderInfoVO.setId(SnowFlakeUtil.getId()).
                    setCarEnterId(firstEnterLog.getId()).
                    setIpAddr(parkArmInfo.getIpAddr()).
                    setSerialNo(parkArmInfo.getSerialNo()).
                    setParkCode(parkArmInfo.getParkCode()).
                    setParkName(parkArmInfo.getParkName()).
                    setEnterTime(firstEnterLog.getCarEnterTime()).
                    setOutTime(DateUtils.dateToString(endDate, "yyyy-MM-dd HH:mm:ss")).
                    setType(firstEnterLog.getCarTypeNo()).
                    setCarTypeId(carTypeId).
                    setPlateNo(firstEnterLog.getPlateNo()).
                    setChargeDuration((int) DateUtils.timeDiff2(carEnterTime, endDate)).
                    setArmName(parkArmInfo.getArmName()).
                    setOrderStatus("1").
                    setChargeTotalFee(String.valueOf(chargeFee)).
                    setChargeUnpaid(String.valueOf(unPayFee)).
                    setChargePaid(String.valueOf(paidFee)).
                    setChargeDue(String.valueOf(unPayFee)).
                    setChargePrepaid(String.valueOf(paidFee)).
                    setTotalDiscAmt((int) map.get("totalDiscAmt")).
                    setEnterArmName(firstEnterLog.getArmName()).
                    setImgPath(firstEnterLog.getImgPath()).
                    setOssImgPath(firstEnterLog.getOssImgPath());
            //加上工作站名和岗亭人账户
            String workUser = parkWorkStationService.getStationOperationNameByParkArm(parkArmInfo);
            if (ObjectUtil.isNotEmpty(workUser)) {
                parkOrderInfoVO.setWorkName(parkArmInfo.getWorkStationName());
                parkOrderInfoVO.setSysUserName(workUser);
            }
            return parkOrderInfoVO;
        } catch (Exception e) {
            log.error("算费异常"+e.getMessage());
            throw new JddException(ResultCode.INTERNAL_SERVER_ERROR,e.getMessage());
        }
    }

    /**
     * @param carEnterLogId 车辆类型Id
     * @return java.util.Map
     * @description: 根据入场记录查询订单信息, 查询支付总金额
     * @author liujunwei@jdd966.com
     * @date 2021/4/26 15:22
     */
    private Map getOrderInfo(String carEnterLogId) {
        Map map = new HashMap(3);
        //根据最近的入场记录id查询对应的支付订单
        QueryWrapper<ParkOrderInfo> orderWrapper = new QueryWrapper<>();
        orderWrapper.eq("park_log_id", carEnterLogId);
        orderWrapper.eq("order_status", "2");
        orderWrapper.orderByDesc("create_time");
        List<ParkOrderInfo> orderInfos = parkOrderInfoMapper.selectList(orderWrapper);
        // 已支付订单总费用
        int orderTotalFee = 0;
        //总优惠金额
        int totalDiscAmt = 0;
        for (ParkOrderInfo orderInfo : orderInfos) {
            int discAmt = getIntegerFromString(orderInfo.getTotalDiscAmt()) + getIntegerFromString(orderInfo.getCouponAmt());
            int fee = getIntegerFromString(orderInfo.getChargePaid()) + discAmt;
            orderTotalFee = orderTotalFee + fee;
            totalDiscAmt = totalDiscAmt + discAmt;
        }
        map.put("orderInfoList", orderInfos);
        map.put("paidFee", orderTotalFee);
        map.put("totalDiscAmt", totalDiscAmt);
        return map;
    }

    /**
     * 得到车辆类型id
     *
     * @param parkCode  车场编号
     * @param carNature 车辆性质
     * @return 车辆类型id
     * @date 2021-07-28
     * @author: liuyaowen
     */
    private String getCarTypeId(String parkCode, String carNature) {
        LambdaQueryWrapper<ConfigCarType> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ConfigCarType::getParkCode, parkCode);
        List<ConfigCarType> list = configCarTypeMapper.selectList(queryWrapper);
        for (ConfigCarType configCarType : list) {
            List<String> carTypes = Arrays.asList(configCarType.getCarTypeNo().split(","));
            if (carTypes.contains(carNature)) {
                return configCarType.getId();
            }
        }
        return null;
    }


    private int getIntegerFromString(String str) {
        if (StringUtils.isBlank(str)) {
            return 0;
        }
        return Integer.valueOf(str);
    }

    /**
     * 得到车辆最新一批入场纪录,车牌号不为空时,查询有牌车的入场纪录,车牌号为空时,查询无牌车的入场纪录
     *
     * @param plateNo  车牌号
     * @param openId   微信openid
     * @param parkCode 车场编号
     * @return 入场纪录
     */
    @Override
    public List<ParkCarEnterLog> getParkCarEnterLog(String plateNo, String openId, String parkCode) {
        List<ParkCarEnterLog> carAllEnterLogs;
        // 得到车辆所有的入场纪录
        if (StringUtils.isNotBlank(plateNo)) {
            carAllEnterLogs = parkCarEnterLogMapper.getCarAllNoOutEnterLogs(parkCode, plateNo);
        } else {
            carAllEnterLogs = parkCarEnterLogMapper.getNoPlateCarAllNoOutEnterLogs(parkCode, openId);
        }
        if (CollectionUtil.isEmpty(carAllEnterLogs)) {
            throw new JddException(ResultCode.PARAMETER_ERROR, "车辆入场纪录不存在");
        }
        // 得到车辆最近的一次入场纪录
        ParkCarEnterLog carLatestEnterLog = carAllEnterLogs.get(carAllEnterLogs.size() - 1);
        // 得到最近的相同入场批次的入场纪录。
        carAllEnterLogs = carAllEnterLogs.stream().filter(enterLog -> enterLog.getBatchNumber().equals(carLatestEnterLog.getBatchNumber())).collect(Collectors.toList());
        // 对入场纪录,针对不同的大小车场,进行去重
        // 定义返回列表
        List<ParkCarEnterLog> nonRepetitiveEnterLogs = new ArrayList<>();
        // 遍历入场纪录列表
        for (ParkCarEnterLog parkCarEnterLog : carAllEnterLogs) {
            if (nonRepetitiveEnterLogs.size() == 0) {
                // 如果返回列表为空,将第一条入场纪录放入
                nonRepetitiveEnterLogs.add(parkCarEnterLog);
            } else {
                // 如果返回列表不为空,获取返回结果中的最后一条入场纪录
                ParkCarEnterLog lastCarEnterLog = nonRepetitiveEnterLogs.get(nonRepetitiveEnterLogs.size() - 1);
                // 如果该条纪录的设备序列号与遍历到的入场纪录的设备序列号不同,将被遍历到的入场纪录放入返回列表
                if (!lastCarEnterLog.getSerialNo().equals(parkCarEnterLog.getSerialNo())) {
                    nonRepetitiveEnterLogs.add(parkCarEnterLog);
                } else {
                    nonRepetitiveEnterLogs.set(nonRepetitiveEnterLogs.size() - 1, parkCarEnterLog);
                }
            }
        }
        return nonRepetitiveEnterLogs;
    }


}
